package cz.data.gateway.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cz.data.common.core.DataConstant;
import cz.data.common.redis.service.RedisService;
import cz.data.gateway.admin.dao.GatewayRouteDao;
import cz.data.gateway.model.dto.GatewayRouteVo;
import cz.data.gateway.admin.mapstruct.GatewayRouteMapper;
import cz.data.gateway.admin.events.EventSender;
import cz.data.gateway.admin.service.IGatewayRouteService;
import cz.data.gateway.model.dto.GatewayRouteQueryParam;
import cz.data.gateway.model.dto.RouteDefinitionDto;
import cz.data.gateway.model.entity.GatewayRoute;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.net.URI;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Slf4j
public class GatewayRouteService extends ServiceImpl<GatewayRouteDao, GatewayRoute> implements IGatewayRouteService {

    private static final String CACHE_ROUTES = "gateway:routes";

    @Autowired
    private EventSender eventSender;
    @Resource
    RedisService redisService;
    @Autowired
    GatewayRouteMapper gatewayRouteMapper;

    @Transactional
    @Override
    public boolean add(GatewayRoute gatewayRoute) {
        gatewayRoute.setStatus(DataConstant.TrueOrFalse.TRUE.getKey());
        boolean isSuccess = this.save(gatewayRoute);
        if (isSuccess) {
            // TODO 转化为gateway需要的类型，缓存到redis, 并事件通知各网关应用
            RouteDefinitionDto routeDefinitionDto = gatewayRouteToRouteDefinition(gatewayRoute);
            redisService.hset(CACHE_ROUTES, gatewayRoute.getRouteId(), gatewayRoute);
            eventSender.send(routeDefinitionDto);
        }
        return isSuccess;
    }

    @Override
    public boolean upsert(GatewayRoute gatewayRoute) {
        gatewayRoute.setCreateTime(LocalDateTime.now());
        gatewayRoute.setUpdateTime(LocalDateTime.now());
        boolean success = this.saveOrUpdate(gatewayRoute);
        if (success) {
            RouteDefinitionDto routeDefinitionDto = gatewayRouteToRouteDefinition(gatewayRoute);
            redisService.hset(CACHE_ROUTES, gatewayRoute.getRouteId(), gatewayRoute);
            eventSender.send(routeDefinitionDto);
        }
        return success;
    }

    @Transactional
    @Override
    public boolean delete(String id) {
        GatewayRoute route = this.getById(id);
        if (Objects.isNull(route))
            return false;
        // 删除redis缓存, 并事件通知各网关应用
        redisService.hdel(CACHE_ROUTES, id);
        RouteDefinitionDto routeDefinition = gatewayRouteToRouteDefinition(route);
        routeDefinition.inactiveRoute();
        eventSender.send(routeDefinition);
        return this.removeById(id);
    }

    @Transactional
    @Override
    public boolean update(GatewayRoute gatewayRoute) {
        boolean isSuccess = this.updateById(gatewayRoute);
        if (isSuccess) {
            // 转化为gateway需要的类型，缓存到redis, 并事件通知各网关应用
            RouteDefinitionDto routeDefinitionDto = gatewayRouteToRouteDefinition(gatewayRoute);
            redisService.hset(CACHE_ROUTES, gatewayRoute.getRouteId(), routeDefinitionDto);
            eventSender.send(routeDefinitionDto);
        }
        return isSuccess;
    }

    private RouteDefinitionDto gatewayRouteToRouteDefinition(GatewayRoute gatewayRoute) {
        RouteDefinitionDto routeDefinitionDto = new RouteDefinitionDto();
        routeDefinitionDto.setId(gatewayRoute.getId());
        routeDefinitionDto.setRouteId(gatewayRoute.getRouteId());
        routeDefinitionDto.setUri(URI.create(gatewayRoute.getUri()));
        routeDefinitionDto.setOrder(gatewayRoute.getOrders());
        routeDefinitionDto.setFilters(gatewayRouteMapper.parseFilters(gatewayRoute.getFilters()));
        routeDefinitionDto.setPredicates(gatewayRouteMapper.parsePredicates(gatewayRoute.getPredicates()));
        routeDefinitionDto.activeRoute(gatewayRoute.getStatus());
        return routeDefinitionDto;
    }

    @Override
    public GatewayRoute get(String id) {
        return this.getById(id);
    }

    @Override
    public List<GatewayRouteVo> query(GatewayRouteQueryParam request) {
        QueryWrapper<GatewayRoute> wrapper = new QueryWrapper<>();
        wrapper.eq(StringUtils.isNotBlank(request.getUri()), "uri", request.getUri());
        return this.list(wrapper).stream().map(GatewayRouteVo::new).collect(Collectors.toList());
    }

    @Override
    public Page<GatewayRoute> page(GatewayRouteQueryParam request) {
        QueryWrapper<GatewayRoute> wrapper = new QueryWrapper<>();
        wrapper.lambda().likeRight(StringUtils.isNotBlank(request.getUri()),
                GatewayRoute::getUri, request.getUri());
        wrapper.lambda().like(StringUtils.isNotBlank(request.getDescription()),
                GatewayRoute::getDescription, request.getDescription());
        wrapper.lambda().orderByDesc(GatewayRoute::getCreateTime);
        return page(request.pageable(Page::new), wrapper);
    }

    @Override
    public List<GatewayRoute> releaseRouters() {
        QueryWrapper<GatewayRoute> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(GatewayRoute::getStatus, DataConstant.TrueOrFalse.TRUE.getKey());
        return list(wrapper);
    }

    @Override
    public boolean overload() {
        List<GatewayRoute> routes = this.list();
        Map<String, Object> routeMap = new HashMap<>(routes.size());
        for (GatewayRoute route : routes) {
            RouteDefinitionDto routeDefinitionDto = gatewayRouteToRouteDefinition(route);
            log.info("初始化路由: {}", routeDefinitionDto);
            eventSender.send(routeDefinitionDto);
            routeMap.put(route.getRouteId(), routeDefinitionDto);
        }
        redisService.del(CACHE_ROUTES);
        redisService.hmset(CACHE_ROUTES, routeMap);

        log.info("全局初使化网关路由成功!");
        return true;
    }

    @Override
    public boolean changeStatus(String id, String status) {
        GatewayRoute route = getById(id);
        if (Objects.isNull(route))
            return false;
        UpdateWrapper<GatewayRoute> wrapper = new UpdateWrapper<>();
        wrapper.lambda().eq(GatewayRoute::getId, id);
        wrapper.lambda().set(GatewayRoute::getStatus, status);
        boolean success = update(wrapper);
        if (success) {
            RouteDefinitionDto routeDefinitionDto = gatewayRouteToRouteDefinition(route);
            routeDefinitionDto.activeRoute(status);
            redisService.hset(CACHE_ROUTES, route.getRouteId(), routeDefinitionDto);
            eventSender.send(routeDefinitionDto);
        }
        return success;
    }
}
